可以看到第一个题目还是非常简单,针对流密码的已知明文攻击,题目首先将 flag 加密并给出了密文,随后又加密了 b'a'*36,但是注意到两次加密都是调用了 enc 函数,而每次调用enc函数都会重新用 key 初始化一个 ARC4 对象,因此两次加密所使用密钥流其实是一致的,因此只需要异或 b'a'*36 和对应的密文得到密钥流,再异或 flag 的密文就能解密获得对应 flag 了。不过由于 flag 是 38 个字节,我们还有 2 个字节不能恢复。不过注意到 flag 的格式是 SEECTF,并且加密的时候也是翻转了的,因此不能恢复的两个字节就是 SE
print("Something something zero-knowledge proofs blah blah...") print("Why not just issue the challenge and the verification at the same time? Saves TCP overhead!")
seen_c = set() for round in range(30): w, r = None, None choice = generate_random_boolean() ifnot choice: w = int(input("Enter w: ")) C = int(input("Enter C: ")) if C in seen_c: fail() seen_c.add(C) verify = first_verify else: r = int(input("Enter r: ")) C = int(input("Enter C: ")) if C in seen_c: fail() seen_c.add(C) verify = second_verify ifnot verify(g, p, y, C, w, r): fail() else: print(f"You passed round {round + 1}.") time.sleep(1) print( "You were more likely to get hit by lightning than proof correctly 30 times in a row, you must know the secret right?" ) print(f"A flag for your troubles - {FLAG}")
from pwn import * from Crypto.Util.number import * #sh = process(["python3","main.py"]) sh = remote("win.the.seetf.sg",3002) sh.recvuntil("p = ") p = int(sh.recvuntil("\n")[:-1]) sh.recvuntil("g = ") g = int(sh.recvuntil("\n")[:-1]) sh.recvuntil("y = ") y = int(sh.recvuntil("\n")[:-1])
for i in range(1,31): choices = sh.recvuntil(": ") ifb"Enter w"in choices: print("w") C = inverse(y,p)*pow(g,i,p) w = i sh.sendline(str(w)) sh.recvuntil(": ") sh.sendline(str(C)) elifb"Enter r"in choices: print("r") r = i C = pow(g,r,p) sh.sendline(str(r)) sh.recvuntil(": ") sh.sendline(str(C))
sh.interactive()
OpenEndedRSA
Warri 74 solves / 406 points I was told my RSA implementation is extremely insecure and vulnerable… I’ll make this open ended for yall to take a look…find the vulnerability and I’ll give you the flag!
""" n = 102273879596517810990377282423472726027460443064683939304011542123196710774901060989067270532492298567093229128321692329740628450490799826352111218401958040398966213264648582167008910307308861267119229380385416523073063233676439205431787341959762456158735901628476769492808819670332459690695414384805355960329 e = 65537 c = 51295852362773645802164495088356504014656085673555383524516532497310520206771348899894261255951572784181072534252355368923583221684536838148556235818725495078521334113983852688551123368250626610738927980373728679163439512668552165205712876265795806444660262239275273091657848381708848495732343517789776957423 s = 128507372710876266809116441521071993373501360950301439928940005102517141449185048274058750442578112761334152960722557830781512085114879670147631965370048855192288440768620271468214898335819263102540763641617908275932788291551543955368740728922769245855304034817063220790250913667769787523374734049532482184053 """
from Crypto.Util.number import * from gmpy2 import * n = 102273879596517810990377282423472726027460443064683939304011542123196710774901060989067270532492298567093229128321692329740628450490799826352111218401958040398966213264648582167008910307308861267119229380385416523073063233676439205431787341959762456158735901628476769492808819670332459690695414384805355960329 e = 65537 c = 51295852362773645802164495088356504014656085673555383524516532497310520206771348899894261255951572784181072534252355368923583221684536838148556235818725495078521334113983852688551123368250626610738927980373728679163439512668552165205712876265795806444660262239275273091657848381708848495732343517789776957423 s = 128507372710876266809116441521071993373501360950301439928940005102517141449185048274058750442578112761334152960722557830781512085114879670147631965370048855192288440768620271468214898335819263102540763641617908275932788291551543955368740728922769245855304034817063220790250913667769787523374734049532482184053 p = iroot(s - 4,2)[0] q = n//p m = pow(c,inverse(e,(p-1)*(q-1)),n) print(long_to_bytes(m))
# b'SEE{0dd_3vEN:deadbeef}'
初级难度
Qskates
Warri 9 solves / 491 points Alice and Bob love skating so much, they’ve gotten Eve into it!
Turns out Eve is a skater herself and wants to know Alice’s secret. She’s placed herself right in the middle of their conversation, can you help her figure out the secret?
from qiskit import QuantumCircuit, Aer from numpy.random import randint from Crypto.Util.number import long_to_bytes from Crypto.Cipher import AES from Crypto.Util.Padding import pad import os import hashlib
defencode_message(bits, bases): message = [] for i in range(n): qc = QuantumCircuit(1,1) if bases[i] == 0: if bits[i] == 0: pass else: qc.x(0) else: if bits[i] == 0: qc.h(0) else: qc.x(0) qc.h(0) qc.barrier() message.append(qc) return message
defmeasure_message(message, bases): measurements = [] for q in range(min(n,len(bases))): if bases[q] == 0: message[q].measure(0,0) if bases[q] == 1: message[q].h(0) message[q].measure(0,0) aer_sim = Aer.get_backend('aer_simulator') result = aer_sim.run(message[q], shots=1, memory=True).result() measured_bit = int(result.get_memory()[0]) measurements.append(measured_bit) return measurements
defremove_garbage(a_bases, b_bases, bits): good_bits = [] for q in range(n): if a_bases[q] == b_bases[q]: good_bits.append(bits[q]) return good_bits
defverifyInput(lst): for i in lst: if i != 0and i != 1: returnFalse returnTrue
flag = b"VCTF"*4#pad(open('flag.txt','rb').read(),16) key = hashlib.sha256(''.join([str(i) for i in alice_key]).encode()).digest()[:16] iv = os.urandom(16) cipher = AES.new(key=key, iv=iv, mode=AES.MODE_CBC) enc = cipher.encrypt(flag) print(f'iv = {iv.hex()}') print(f'enc = {enc.hex()}')
whileTrue: message = encode_message(alice_bits, alice_bases) eve_bases = [int(i) for i in input("Enter bases to intercept: ")] assert verifyInput(eve_bases)
eve_results = measure_message(message, eve_bases) print("Measured message:", eve_results) new_bits = [int(i) for i in input("Enter bits to send to Bob: ")] assert verifyInput(new_bits) new_bits += alice_bits.tolist()[len(new_bits):] new_message = encode_message(new_bits, alice_bases) bob_results = measure_message(new_message, bob_bases) alice_key = remove_garbage(alice_bases, bob_bases, alice_bits) bob_key = remove_garbage(alice_bases, bob_bases, bob_results) print(alice_key == bob_key)
from pwn import * from numpy.random import randint from Crypto.Util.number import long_to_bytes from Crypto.Cipher import AES from Crypto.Util.Padding import pad import os import hashlib
#sh = process(["python3","chall.py"]) #context.log_level = 'debug' sh = remote("win.the.seetf.sg",3004) sh.recvuntil("iv =") iv = bytes.fromhex(sh.recvuntil("\n")[:-1].decode()) sh.recvuntil("enc = ") enc = bytes.fromhex(sh.recvuntil("\n")[:-1].decode())
key = ""
sh.recvuntil("Enter bases to intercept:") sh.sendline("0") sharekey = ""
while len(key)<100: sh.recvuntil("Enter bits to send to Bob:") sh.sendline(key+"0") judge = sh.recvuntil("Enter bases to intercept:") ifb"False"in judge: key += "1" sharekey += "1" sh.sendline("0") else: sh.sendline("0") sh.recvuntil("Enter bits to send to Bob:") sh.sendline(key+"1") judge = sh.recvuntil("Enter bases to intercept:") sh.sendline("0") ifb"True"in judge: key += "0" else: key += "0" sharekey += "0" print(sharekey)
key = hashlib.sha256(''.join([str(i) for i in sharekey]).encode()).digest()[:16] cipher = AES.new(key=key, iv=iv, mode=AES.MODE_CBC) flag = cipher.decrypt(enc) print(flag)
p=6277101735386680763835789423207666416083908700390324961279 a=-3 b=2455155546008943817740293915197451784769108058161191238065 order = 6277101735386680763835789423176059013767194773182842284081 E = EllipticCurve(GF(p),[a,b]) G = E(602046282375688656758213480587526111916698976636884684818,174050332293622031404857552280219410364023488927386650641)
data = s.split("\n") #for each in s: each = data[0] r0,s0 = int(each[:48],16),int(each[48:],16) R0 = E.lift_x(Integer(r0))
each = data[2] r2,s2 = int(each[:48],16),int(each[48:],16) R2 = E.lift_x(Integer(r2))
each = data[3] r3,s3 = int(each[:48],16),int(each[48:],16) R3 = E.lift_x(Integer(r3))
each = data[4] r4,s4 = int(each[:48],16),int(each[48:],16) R4 = E.lift_x(Integer(r4))
each = data[5] r5,s5 = int(each[:48],16),int(each[48:],16) R5 = E.lift_x(Integer(r5))
from itertools import permutations from tqdm import tqdm
dic = {} flag = "" index = 0 table = "5347b"+"012689"+"d"+"caef" for i in range(len(data)): each = data[i] r,s = int(each[:48],16),int(each[48:],16) R = E.lift_x(Integer(r)) zG1 = int(r)*(QA - (int(s*pow(r,-1,order)))*R) zG2 = int(r)*(QA - (int(s*pow(r,-1,order)))*-R) if zG1.xy()[0] in dic: flag += dic[zG1.xy()[0]] elif zG2.xy()[0] in dic: flag += dic[zG2.xy()[0]] else: dic[zG1.xy()[0]] = table[index] dic[zG2.xy()[0]] = table[index] flag += table[index] index+=1
table = "5347b"+"012689"+"d"+"caef"
each = data[0] r,s = int(each[:48],16),int(each[48:],16) R = E.lift_x(Integer(r)) u2 = s * pow(r,-1,order)
from string import printable def check(s): for each in s: if each not in "1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm{}_-!@#$": return False return True
for each in tqdm(permutations("012689caef",int(10))): table2 = "5347b"+"".join(i for i in each[:6])+"d"+"".join(i for i in each[6:]) table3 = str.maketrans(table,table2) FLAG = flag.translate(table3) if not check(bytes.fromhex(FLAG).decode('latin1')): continue FLAG = bytes.fromhex(FLAG)+b"5" #print(FLAG) e = bytes_to_long(hashlib.sha1(FLAG).digest()) u1 = -e * pow(r,-1,order) QAt1 = int(u1) * G + int(u2) * R QAt2 = int(u1) * G - int(u2) * R if QAt1.xy()[0] == QA.xy()[0] or QAt2.xy()[0] == QA.xy()[0]: print(FLAG) break
chrs = (string.ascii_letters + string.digits + "_").encode() avg = sorted(chrs)[len(chrs) // 2] - 1 print(avg) print([x - avg for x in sorted(chrs)]) # within [-37, 37]
M = 13**37 C = int.from_bytes(b"SEE{" + b"\x00" * 23 + b"}", "big")
P = PolynomialRing(ZZ, "ap", 23) aps = P.gens() aa = [ap + avg for ap in aps] f = C + sum([a * 256**i for i, a in enumerate(aa)]) * 256 print(f)
L = matrix(f.coefficients()).T L = block_matrix([[1,L],[0,M]]) scale = [1]*24 + [2**20] Q = diagonal_matrix(scale) L *= Q L = L.BKZ(block_size=25) L /= Q
for row in L: if row[-2] < 0: row = -row if row[-1] == 0and row[-2] == 1: print(row) print(f(*row[:-2]) % M == 0) aa = [x + avg for x in row[:-2]][::-1] flag = b"SEE{" + bytes(aa) + b"}" assert int.from_bytes(flag, "big") % M == 0 print(flag)
from fpylll import IntegerMatrix, LLL from fpylll.fplll.gso import MatGSO from fpylll.fplll.enumeration import Enumeration
sols = []
L[:, -1] *= 2**10
A = IntegerMatrix.from_matrix(L.change_ring(ZZ)) LLL.reduction(A) MG = MatGSO(A) MG.update_gso() sol_cnt = 10000 enum = Enumeration(MG, sol_cnt) size = int(L.nrows()) bound = 37 answers = enum.enumerate(0, size, (size * bound**2), 0, pruning=None) for _, s in answers: v = IntegerMatrix.from_iterable(1, A.nrows, map(int, s)) sv = v * A
if sv[0, -2] in (-1, 1) and sv[0,-1] == 0: neg = sv[0, -2] sol = [neg * sv[0, i] for i in range(23)] assert f(*sol) % M == 0 aa = [x + avg for x in sol][::-1] flag = b"SEE{" + bytes(aa) + b"}" assert int.from_bytes(flag, "big") % M == 0 print(flag) try: if re.fullmatch(r"SEE{\w{23}}", flag.decode()): print("FOUND") break except UnicodeDecodeError: pass # SEE{luQ5xmNUKgEEDO_c5LoJCum}
Romeo and Juliet
Neobeo 5 solves / 495 points Romeo and Juliet have opened a secure channel where they can feel free to say anything they want to each other.
deffast_polynomial_gcd(a0, a1): """ Uses a divide-and-conquer algorithm (HGCD) to compute the polynomial gcd. More information: Aho A. et al., "The Design and Analysis of Computer Algorithms" (Section 8.9) :param a0: the first polynomial :param a1: the second polynomial :return: the polynomial gcd """ # TODO: implement extended variant of half GCD? assert a0.parent() == a1.parent()
for _ in range(100): E = EllipticCurve(j = j) print('-'*63) print(f'You are in Town {j}.') if str(j) in'31415926535897932384626433832795': print(f'There is a flag in this town: {flag}') neighbours = [E.isogeny_codomain(m).j_invariant() for m in E(0).division_points(2)] neighbours = sorted(set(neighbours) - {j}) roadmsg = 'is only 1 road'if len(neighbours) == 1elsef'are {len(neighbours)} roads' print(f'There {roadmsg} out of here:') for i,n in enumerate(neighbours): print(f'* Road [{i+1}] leads to Town {n}') print('Which road would you like to take?') try: j = neighbours[int(input())-1] except (ValueError, IndexError): print('Invalid road.') pass
from Crypto.Util.number import getPrime from Crypto.Util.Padding import pad from Crypto.Cipher import AES from secrets import randbelow from hashlib import sha256 from math import isqrt import os
flag = os.environ.get('FLAG', 'SEE{not_the_real_flag}').encode()
p, q = getPrime(512), getPrime(512) n = (p * q)**3 a = randbelow(3**1337); A = pow(3, a, n) b = randbelow(3**1337); B = pow(3, b, n) shared = pow(A, b, n) assert shared == pow(B, a, n)
import time from subprocess import check_output from re import findall
defflatter(M): # compile https://github.com/keeganryan/flatter and put it in $PATH z = "[[" + "]\n[".join(" ".join(map(str, row)) for row in M) + "]]" ret = check_output(["flatter"], input=z.encode()) return matrix(M.nrows(), M.ncols(), map(int, findall(b"-?\\d+", ret)))
defsmall_roots(self, X=None, beta=1.0, epsilon=None, **kwds): from sage.misc.verbose import verbose from sage.matrix.constructor import Matrix from sage.rings.real_mpfr import RR
N = self.parent().characteristic()
ifnot self.is_monic(): raise ArithmeticError("Polynomial must be monic.")
beta = RR(beta) if beta <= 0.0or beta > 1.0: raise ValueError("0.0 < beta <= 1.0 not satisfied.")
m = max(beta**2 / (delta * epsilon), 7 * beta / delta).ceil() verbose("m = %d" % m, level=2)
t = int((delta * m * (1 / beta - 1)).floor()) verbose("t = %d" % t, level=2)
if X isNone: X = (0.5 * N ** (beta**2 / delta - epsilon)).ceil() verbose("X = %s" % X, level=2)
# we could do this much faster, but this is a cheap step # compared to LLL g = [x**j * N ** (m - i) * f**i for i in range(m) for j in range(delta)] g.extend([x**i * f**m for i in range(t)]) # h
B = Matrix(ZZ, len(g), delta * m + max(delta, t)) for i in range(B.nrows()): for j in range(g[i].degree() + 1): B[i, j] = g[i][j] * X**j
B = flatter(B)
f = sum([ZZ(B[0, i] // X**i) * x**i for i in range(B.ncols())]) R = f.roots()
ZmodN = self.base_ring() roots = set([ZmodN(r) for r, m in R if abs(r) <= X]) Nbeta = N**beta return [root for root in roots if N.gcd(ZZ(self(root))) >= Nbeta] defcopp_factor(sp, leak=6): for tb in range(1 << leak): print("copp", tb, int(time.time())) shift = 256 - leak P = Zmod(pq)["x"] x = P.gen() f = sp.int**2 + (tb << shift) + x X = 2 ** (256 - leak) beta = 0.499 eps = 0.01 # rs = f.small_roots(X=X, beta=beta, epsilon=eps) rs = small_roots(f, X=X, beta=beta, epsilon=eps) if len(rs): return sp.int**2 + (tb << shift) + int(rs[0])
for spci in results: print(spci) print(i, p := copp_factor(Bin(spci, 256))) if p: q = pq // p print(p, q) assert p * q == pq break